package com.daikun.common.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.daikun.common.enums.BaseResultEnum;
import com.daikun.common.enums.SecurityEnum;
import com.daikun.common.enums.SecurityTypeEnum;
import com.daikun.common.exception.BizException;
import com.daikun.common.model.BaseModel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 加解密工具类
 * <a href="https://xunmotech.feishu.cn/wiki/NpEjwoCfkiTrc0kIl91ci6Z3nMd">加解密接口文档</a>
 * @author earl created 2025-05-20
 * @version init 0.0.1
 */
@Slf4j
public class BaseSecurityUtil {

    // 默认超时时间
    private static final int TIMEOUT = 3000;

    /**
     * 数据加解密
     * @param item 必传 - {@link  SecurityEnum}
     * @param typeItem 必传 - {@link SecurityTypeEnum}
     * @param data 必传 - 需要加解密的数据
     * @param uuid 必传 - 唯一标识，方便做后期统计，有 uuid 可以传 uuid，若没有 uuid 可以根据场景传其他数据
     *             定时器调用可以传 timer，MQ 队列调用可以传 kafka，后台调用可以传 用户名称，其他调用可以传 system 等等
     * @return key 为原文，val 对应的是加解密服务返回的数据
     */
    public static Map<String, SecurityModel> invoke(SecurityEnum item, SecurityTypeEnum typeItem, String data, String uuid) {
        return invoke(item, typeItem, ListUtil.toList(data), uuid);
    }

    public static Map<String, SecurityModel> invoke(SecurityEnum item, SecurityTypeEnum typeItem, List<String> list, String uuid) {
        if (item == null) {
            return MapUtil.empty();
        }
        list = (list == null) ? new ArrayList<>() : list.stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
        if (list.isEmpty()) {
            return MapUtil.empty();
        }
        Pair<String, String> param = item.createParam(list, typeItem, uuid);
        if (param == null) {
            return CollUtil.isEmpty(list) ? MapUtil.empty() : list.stream().collect(Collectors.toMap(e -> e, e -> new SecurityModel(e, item.isFlag())));
        }
        JSON resJson = sendRequest(param);
        return data(resJson, item.isFlag());
    }

    private static JSON sendRequest(Pair<String, String> param) {
        // noinspection HttpUrlsUsage
        try (HttpResponse resp = HttpUtil
                .createPost(StrUtil.blankToDefault(SpringUtil.getProperty("encryptServiceUrl"), "http://test-vencryption.daikuns.com:8090") + param.getKey())
                .header("Content-Type", "application/json; charset=utf8")
                .setConnectionTimeout(TIMEOUT).setReadTimeout(TIMEOUT).body(param.getValue()).execute()) {
            if (resp == null || resp.getStatus() != 200) {
                log.info("加解密请求失败-> param = {}", param);
                throw new BizException(BaseResultEnum.SECURITY_FAIL_01);
            }
            if (!JSONUtil.isTypeJSON(resp.body())) {
                log.info("加解密请求失败，返回数据非 json 结构体 -> param = {}", param);
                throw new BizException(BaseResultEnum.SECURITY_FAIL_02);
            }
            if (!"0".equalsIgnoreCase(JSONUtil.parseObj(resp.body()).getStr("code"))) {
                log.info("加解密请求失败，返回状态码非 0 -> body = {}, param = {}", resp.body(), param);
                throw new BizException(BaseResultEnum.SECURITY_FAIL_03);
            }
            String data = JSONUtil.parseObj(resp.body()).getStr("data");
            return (JSON)(JSONUtil.isTypeJSONArray(data) ? JSONUtil.parseArray(data) : JSONUtil.parseObj(data));
        } catch (Exception e) {
            log.info("加解密接口调用失败-> param = {}, msg = {}", new Object[] { param, e.getMessage(), e });
            throw new BizException(BaseResultEnum.SECURITY_FAIL_04);
        }
    }

    private static Map<String, SecurityModel> data(JSON obj, boolean flag) {
        if (Objects.isNull(obj))
            throw new BizException(BaseResultEnum.SECURITY_FAIL_06);
        Map<String, SecurityModel> result = new HashMap<>();
        if (JSONUtil.isTypeJSONArray(obj.toString())) {
            for (Object o : JSONUtil.parseArray(obj)) {
                SecurityModel model = JSONUtil.toBean(o.toString(), SecurityModel.class);
                result.put(flag ? model.plainValue : model.encryptKey, model);
            }
        } else {
            SecurityModel model = JSONUtil.toBean(obj.toString(), SecurityModel.class);
            result.put(flag ? model.plainValue : model.encryptKey, model);
        }
        return result;
    }

    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class SecurityModel extends BaseModel {
        /** 密文 */
        private String encryptKey;
        /** 明文 */
        private String plainValue;
        /** 脱敏数据 */
        private String offValue;

        public SecurityModel(String data, boolean flag) {
            this.offValue = null;
            this.encryptKey = flag ? null : data;
            this.plainValue = flag ? data : null;
        }
    }
}